============================================================================= Anomie's C4 Chip Doc $Revision: 1157 $ $Date: 2007-07-12 16:39:41 -0400 (Thu, 12 Jul 2007) $ ============================================================================= C4 info ======== The C4 chip maps its registers and memory at $6000-$7fff in banks $00 through $3f. Known portions of the memory space are: $6000-$6bff: Generic RAM $7f40-$7f47: See below $7f48: Unknown, always seems to be 01 $7f4c: ??? (seems to be cleared for wireframe and lines, set for OAM) $7f4d: Subcommand specifier for command 00 $7f4e: Unknown, always seems to be 00 $7f4f: Command specifier, when this is written the C4 does something $7f5e: Bit 6 seems to be set when the C4 is working, and clear otherwise $7f80-$7faf?: A number of 3-byte registers Certain registers are 'protected' by waiting for bit 6 of $7f5e to be 0. In particular, in MMX2 a single subroutine is used for activation of all C4 commands, which does approximately the following: protect using $7f5e disable NMI and timers write the subcommand to $7f4d write #$00 to $7f4e write an unknown value (typically 1) to $7f48 protect using $7f5e write the command to $7f4f protect using $7f5e re-enable NMI and timers $7f40-$7f47 are used to load data from RAM. The registers are as follows: $7f40-2: Source address $7f43-4: Count $7f45-6: Dest address, so far always $6600 $7f47: Begin transfer (protect before and after with $7f5e) C4 Commands =========== 00: Sprite functions 01: Draw wireframe 05: Propulsion (?) 0d: Set vector length 10: Convert polar coordinates to rectangular 13: Convert polar coordinates to rectangular 15: Pythagorean 1f: atan 22: Trapaziod 25: Multiply 2d: Transform Coords 40: Sum (test) 54: Square (test) 5c: Immediate Reg (test) 89: Immediate ROM (test) In addition to the four marked "test", there are a number of other testing commands: if $7f4d is $0e, then any command with bits 00????00 will write command>>2 to $7f80. 00: Sprite functions -------------------- When $00 is written to $7f4f, the subfunction specified in $7f4d is performed. 00: Build OAM 03: Scale/Rotate 05: Transform Lines 07: Scale/Rotate 08: Draw wireframe 0b: Disintergrate 0c: Wave 00: Build OAM Converts a description of a large composite sprite into OAM-like data. Input: $6220+: Composite sprite info table $6620: Number of composite sprites $6621-2: global X offset (signed) $6623-4: global Y offset (signed) $6625: ??? (always 0?) $6626: First sprite to write $6627-8: ??? ($6626 << 2, OAM offset of first sprite?) $6629: ??? ($6626 >> 2, OAM table 2 offset of first sprite - 0x200?) Output: $6000-$621f: OAM image Composite sprite info table begins at $6220 Composite sprite info: 16 bytes +00-1: Sprite X position (signed) +02-3: Sprite Y position (signed) +04: Sprite attribute byte bit 0: OAM Name high bit bits 1-3: palette bits 4-5: prio) - XXX: Are bits 6 and 7 unused, or what? +05: OAM Name +06: Sprite attribute byte 2 bit 6: x flip bit bit 7: y flip bit - XXX: Are bits 0-5 unused, or what? +07-9: Pointer to OAM sprite data: bank in +09, addr in +07 and +08 +0a-f: unused OAM sprite data in ROM: +00: Number of sprites this composite uses - If 0, only one sprite and all parameters below are assumed 0 except Large size sprite bit is assumed 1. +01: bit 5: Large size sprite bit bit 6: x flip bit toggle bit 7: y flip bit toggle +02: X offset diff for this sprite (signed) +03: Y offset diff for this sprite (signed) +04: OAM Name diff [repeat +01-4 for the number of sprites specified] OAM Ram is constructed in a $220-byte buffer beginning at $6000. All sprites beginning with that specified by $6626 will be written. They are first 'cleared' by setting the Y position of these sprites to $e0 in the pseudo-OAM at $6000-$621f. Then the composite sprite structures are moved through sequentially until either we run out of structures or we run out of OAM. For each sprite, we set OAM as follows: X = (Sprite X position) - (global X offset) + (X offset diff*) Y = (Sprite Y position) - (global Y offset) + (Y offset diff*) Name = ((OAM Name) + (OAM Name diff)) & $ff XXX: is the high bit taken from either sprite attribute byte? VFlip = (y flip bit) xor (y flip bit toggle) HFlip = (x flip bit) xor (x flip bit toggle) Prio = Sprite attribute byte prio Pal = Sprite attribute byte palette Size = Large size sprite bit * NOTE: X/Y offset diff is instead subtracted if x/y flip bit is set. In this case, either 8 or 16 is also subtracted, depending on the Large size sprite bit. 03: Scale/Rotate Beginning with a 4-bit image in $6600, scales and rotates it, converts from packed-pixel to bitplane, and outputs it to $6000. Input: $6600+: 4-bit packed-pixel image $7f80-1: Rotation angle 0-512 $7f82: ??? (MSB of above?) $7f83-5: ??? (center of rotation X? always seems to be half the width) $7f86-8: ??? (center of rotation Y? always seems to be half the height) $7f89: Width $7f8a-b: ??? (MSBs of above?) $7f8c: Height $7f8d-e: ??? (MSBs of above?) $7f8f-0: X scale factor 0bbb.bbbbbbbbbbbb $7f91: ??? (MSB of above?) $7f92-3: Y scale factor 0bbb.bbbbbbbbbbbb $7f94: ??? (MSB of above?) $7f95: ??? (Seems to correspond with the first row of tiles actually read) $7f96: ??? (Seems to correspond with the amount of data read) $7f97: ??? (always #$00?) Output: $6000+: Transformed image in normal snes bitplane format To determine the source coordinate for an output coordinate: / Sx \ / Cx \ / Xs*cos(R) -Ys*sin(R) \ / Ox - Cx \ | | = | | + | | * | | \ Sy / \ Cy / \ Xs*sin(R) Ys*cos(R) / \ Oy - Cy / Ox/Oy = Output coord X and Y Sx/Sy = Source coord X and Y Cx/Cy = Rotation center X and Y Xs/Ys = X and Y scale factors R = rotation angle Source coords outside the source image are color 0. 05: Transform Lines Converts a list of points (at $6000) and a list of vertex-pairs (at $6b02) to a list of distance-and-directions at $6600. It also writes the transformed X and Y coordinates back the list of points at $6000. Input: $6000+: List of points $6b00-1: number of lines $6b02+: List of vertex-pairs $7f80-1: number of vertex-pairs $7f82: ??? (MSB of above?) $7f83: X rotation $7f84-5: ??? (MSBs of above?) $7f86: Y rotation $7f87-8: ??? (MSBs of above?) $7f89: Z rotation $7f8a: ??? (MSBs of above? Perspective factor?) $7f8b: ??? (MSB of above?) $7f8c: Scale factor $7f8d-e: ??? (MSBs of above?) Output: $6000+: Transformed points (X and Y) $6600+: List of distance-and-directions Each entry in the list of points is 16 bytes: +0: fractional part of X? +1-2: X coord (little-endian, oddly enough) +4: fractional part of Y? +5-6: Y coord +8: fractional part of Z? +9-a: Z coord Each entry in the list of vertex-pairs is 2 bytes: +0: index of point 1 +1: index of point 2 The points should be transformed as normal, except that the $95 is subtracted from the Z coordinate before rotation, and a perspective effect is generated by multiplying the output X and Y by $95/($90*(Z+$95)) Each entry in the list of distance-and-directions is 8 bytes: +0-1: Distance +2-3: delta-X * 256 +4: ??? (MSB?) +5-6: delta-Y * 256 +7: ??? (MSB?) Note that either delta-X or delta-Y is 1, and the other is <= 1. Basically, transform all the points, then go over the list of vertex-pairs and generate the distance-and-directions. 07: Scale/Rotate Beginning with a 4-bit packed-pixel image, scales and rotates it, converts from packed-pixel to bitplane, and outputs it to $6000. Note that 2 tiles are left blank at the end of each row, otherwise it's pretty much the same as 03 Scale/Rotate. Input: $6600+: 4-bit packed-pixel image $7f80-1: Rotation angle 0-512 $7f82: ??? (MSB of above?) $7f83-5: ??? (center of rotation X? always seems to be half the width) $7f86-8: ??? (center of rotation Y? always seems to be half the height) $7f89: Width $7f8a-b: ??? (MSBs of above?) $7f8c: Height $7f8d-e: ??? (MSBs of above?) $7f8f-0: X scale factor 0bbb.bbbbbbbbbbbb $7f91: ??? (MSB of above?) $7f92-3: Y scale factor 0bbb.bbbbbbbbbbbb $7f94: ??? (MSB of above?) $7f95: ??? (Seems to correspond with the first row of tiles actually read) $7f96: ??? (Seems to correspond with the amount of data read) $7f97: ??? (always #$00?) Output: $6000+: Transformed image in normal snes bitplane format To determine the source coordinate for an output coordinate: / Sx \ / Cx \ / Xs*cos(R) -Ys*sin(R) \ / Ox - Cx \ | | = | | + | | * | | \ Sy / \ Cy / \ Xs*sin(R) Ys*cos(R) / \ Oy - Cy / Ox/Oy = Output coord X and Y Sx/Sy = Source coord X and Y Cx/Cy = Center X and Y Xs/Ys = X and Y scale factors R = rotation angle Source coords outside the source image are color 0. 08: Draw wireframe Renders a 4-color wireframe object into $6300-$6bff. Thus, if you want to use this as a sprite, you'll have to fill in the two missing bitplanes yourself. Input: $6295: Number of lines in the object $6296: ??? High byte of # of lines? $7f80-2: Pointer to the list of line structures $7f86: X rotation $7f87: Y rotation $7f88: Z rotation $7f8f: ??? (fractional part of scale factor?) $7f90: Scale factor for the figure $7f91: ??? (MSB of scale factor?) $7fa4-6: ??? (always 001000) Output: $6300+: Rendered image in normal snes bitplane format The line structure is 5 bytes: +0-1: Pointer to point 1 data, in bigendian format +2-3: Pointer to point 2 data, in bigendian format +4: Color for this line, 0-3 The point structure is simple, 6 bytes: +0-1: X coordinate, in bigendian format +2-3: Y coordinate, in bigendian format +4-5: Z coordinate, in bigendian format Basically, just iterate over the lines. Take the two points, transform them as specified, throw away the Z after transformation, and draw the line in the given color into $6300+ (clipped to box 96 pixels (12 tiles) on a side). 0b: Disintegrate This "disintegrates" a sprite. The effect is that of inserting rows/columns of color 0 (transparent) in between each row/column of the actual image. The source sprite (4-bit packed pixel) is loaded from $6600, and the disintegrated sprite (normal snes format) is output to $6000. Input: $6600+: 4-bit packed-pixel image $7f80-1: ??? (Center X?) $7f82: ??? (MSB of above?) $7f83-4: ??? (Center Y?) $7f85: ??? (MSB of above?) $7f86-7: X scale factor (bbbbbbbb.bbbbbbbb format) $7f88: ??? (MSB of above?) $7f89: Width $7f8a-b: ??? (MSBs of above?) $7f8c: Height $7f8d-e: ??? (MSBs of above?) $7f8f-0: Y scale factor (bbbbbbbb.bbbbbbbb format) $7f91: ??? (MSB of above?) Output: $6000+: Transformed image in normal snes bitplane format Basically, just do the inverse of a normal scaling operation with the origin at the center. IOW, instead of taking each pixel of the destination and mapping it into the source, take each pixel of the source and map to the destination, leaving all extra destination pixels 0. 0c: Wave Generates a 4-color 32x5-tile wave at $6000. This, if you want to use this in a 4-bit graphics plane, you'll need to supply the other 2 planes yourself. Input: $6a00-f: even dither tile $6a10-f: odd dither tile $6b00+: wave depth table, 128 bytes $7f80-2 ??? (always the [$7f83]th depth entry?) $7f83: starting offset into wave depth table $7f84-5 ??? (unused MSBs of above?) Output: $6000+: Rendered image in normal snes bitplane format The top pixel of each column is depth -24, the bottom is 16. For all pixels deeper than the wave depth, the pixel is color 2. For all pixels at or above the wave depth and less than 8 above the wave depth, the appropriate pixel is chosen from the appropriate dither tile. For pixels more than 8 above the wave depth, the color is 0. 01: Draw wireframe ------------------ Renders a 4-color wireframe object into $6300-$6bff. The main (only?) difference between this and the sprite wireframe function is that this clears the buffer before rendering. Input: $6295: Number of lines in the object $6296: ??? High byte of # of lines? $7f80-2: Pointer to the list of line structures $7f86: X rotation $7f87: Y rotation $7f88: Z rotation $7f8f: ??? (fractional part of scale factor?) $7f90: Scale factor for the figure $7f91: ??? (MSB of scale factor?) $7fa4-6: ??? (always 001000) Output: $6300+: Rendered image in normal snes bitplane format The line structure is 5 bytes: +0-1: Pointer to point 1 data, in bigendian format +2-3: Pointer to point 2 data, in bigendian format +4: Color for this line, 0-3 The point structure is simple, 6 bytes: +0-1: X coordinate, in bigendian format +2-3: Y coordinate, in bigendian format +4-5: Z coordinate, in bigendian format Basically, just iterate over the lines. Take the two points, transform them as specified, throw away the Z after transformation, and draw the line in the given color into $6300+ (clipped to box 96 pixels (12 tiles) on a side). 05: Propulsion (?) ------------------ Input: $7f80: ??? (fractional part of A?) $7f81-2: input (A) $7f83-4: input (B) $7f82: ??? (MSB of above?) Output: $7f80-1: output (O) $7f82: ??? (MSB of above?) I'm not sure what exactly this is doing, but it seems to be: O=(0x10000/B*A)>>8 0d: Set vector length --------------------- This takes an XY vector and a length, and outputs the new vector pointing in the same direction but with the new length. Input: $7f80-1: X $7f82: ??? (MSB of above?) $7f83-4: Y $7f85: ??? (MSB of above?) $7f86-7: length $7f88: ??? (MSB of above?) Output: $7f89-a: outX $7f8b: ??? (MSB of above?) $7f8c-d: outY $7f8e: ??? (MSB of above?) Basically: outX = X * length / sqrt(X**2 + Y**2) * 0.98 outY = Y * length / sqrt(X**2 + Y**2) * 0.99 10: Convert polar to rectangular -------------------------------- This is really much like $13, except it doesn't do the decimal portion. Input: $7f80-1: angle $7f82: ??? (MSB of above?) $7f83-4: length $7f85: ??? (MSB of above?) Output: $7f86-8: output X $7f89-b: output Y Basically, the code does: X=length*cos(angle)*2 Y=length*sin(angle)*126/64 13: Polar to rectangular ------------------------ Apparently, this converts a length-and-angle into horizontal and vertical components. Input: $7f80-1: angle $7f82: ??? (MSB of above?) $7f83-4: length $7f85: ??? (MSB of above?) Output: $7f86-8: output X (format bbbbbbbbbbbbbbb.bbbbbbbb) $7f89-b: output Y (format bbbbbbbbbbbbbbb.bbbbbbbb) Basically, the code does: X=length*cos(angle)*2 Y=length*sin(angle)*2 15: Pythagorean --------------- This is the classic application of the Pythagorean Theorem. Input: $7f80-1: input X $7f82: ??? (MSB of above?) $7f83-4: input Y $7f85: ??? (MSB of above?) Output: $7f80-1: distance $7f82: ??? (MSB of above?) Specifically, distance=sqrt(X**2+Y**2) 1f: atan -------- This calculates the inverse tangent (atan) function. Input: $7f80-1: opposite $7f82: unused? $7f83-4: adjacent $7f85: unused? Output: $7f86-7: output angle $7f88: ??? (MSB of above?) { 0x80 if X is 0 and Y>0 angle = 0x1ff & { 0x180 if X is 0 and Y<=0 { atan(Y/X)/(2*PI)*512+0x100 if X<0 { atan(Y/X)/(2*PI)*512 otherwise 22: Trapeziod ------------- Plots a trapezoid with the parallel edges horizontal, and the other edges at specified angles. The X positions of the non-parallel edges are written into into $6800 (left) and $6900 (right). The trapaziod extends downward and rightward from (Source X, Source Y), with the left edge angled at (Angle 1) from vertical and the right at (Angle 2). Input: $7f80-1: ScrollX $7f82: ??? (MSB of above?) $7f83-4: ScrollY $7f85: ??? (MSB of above?) $7f86-7: SourceX position of upper-left corner $7f88: ??? (MSB of above?) $7f89-a: SourceY position of upper-left corner $7f8b: ??? (MSB of above?) $7f8c-d: Angle1 (range 0-0x1ff) $7f8e: ??? (MSB of above?) $7f8f-0: Angle2 (range 0-0x1ff) $7f91: ??? (MSB of above?) $7f92: ??? (LSBs of below?) $7f93-4: Length of top base (B) Output: $6800+: Left edge X positions $6900+: Right edge X positions Coordinates are clipped to the range 0<=x<=255. For emulation, you may want to set the left edge to 1 if both left and right are <0, and the right edge to 254 if both left and right are >255. The real C4 may or may not do this (or it may do something else), as those edge pixels are usually hidden off the edge of the TV screen. Description is easiest with a picture: (SourceX-ScrollX, SourceY-ScrollY) +----{B}---- /A1 \A2 / \ Above the source point, left=1 and right=0 (or something to the same effect). Below the source point, formulas for the edges are: left X = tan(Angle1)*(Y + ScrollY - SourceY) - (ScrollX) + (SourceX) right X = tan(Angle2)*(Y + ScrollY - SourceY) - (ScrollX) + (SourceX) + B 25: Multiply ------------ Input: $7f80-2: Multiplicand A $7f83-5: Multiplicand B Output: $7f80-2 or $7f80-5: Product This seems to multiply the 3-byte word in $7f80 with the 3-byte word in $7f83, and write the result back to $7f80. That's all. MMX2 uses it at that infamous point in the Overdrive Ostrich stage. 2d: Transform Coords -------------------- Transforms and projects coordinates. Input: $7f80: ??? (fractional part of below?) $7f81-2: X $7f83: ??? (fractional part of below?) $7f84-5: Y $7f86: ??? (fractional part of below?) $7f87-8: Z $7f89: X rotation $7f8a: Y rotation $7f8b: Z rotation $7f8c-e: ??? (always 001000) $7f8f: ??? (fractional part of below?) $7f90: Scale factor $7f91: ??? (MSB of above?) Output: $7f80-1: X $7f82: ??? (MSB of above?) $7f83-4: Y $7f85: ??? (MSB of above?) Basically, this just rotates and scales a point about the origin and projects it onto the XY plan by throwiung away the Z coordinate. IOW, this is exactly the transformation done by command 00 subcommand 08. 40: Sum ------- This is a testing function. It adds the bytes at $6000-$67ff, and outputs the result to $7f80-1 (maybe $7f80-2, MMX2 doesn't check it). Input: $6000+: Input bytes Output: $7f80-1: Sum $7f82: ??? (MSB of above?) 54: Square ---------- This is a function only used for testing. It squares the value in $7f80-2. Input: $7f80-2: 24-bit value to square (signed) Output: $7f83-8: 48-bit output 5c: Immediate Reg ----------------- This is a testing function. It fills $6000-$602f with the following data: Input: none Output: $6000-3: $00, $00, $00, $ff $6004-7: $ff, $ff, $00, $ff $6008-b: $00, $00, $00, $ff $600c-f: $ff, $ff, $00, $00 $6010-3: $ff, $ff, $00, $00 $6014-7: $80, $ff, $ff, $7f $6018-b: $00, $80, $00, $ff $601c-f: $7f, $00, $ff, $7f $6020-3: $ff, $7f, $ff, $ff $6024-7: $00, $00, $01, $ff $6028-b: $ff, $fe, $00, $01 $602c-f: $00, $ff, $fe, $00 89: Immediate ROM ----------------- This is a testing function. It sets $7f80-2 to $054336 Input: none Output: $7f80-2: $054336 ============================================================================= HISTORY Version 1.5: * Actually put a version number on it, and started keeping track of history.